package com.yirui.serialcomm.core.protocol;

import com.yirui.serialcomm.core.domain.SimpleSerialCommPacket;
import com.yirui.serialcomm.core.domain.StreamType;
import com.yirui.serialcomm.core.util.HexUtils;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.nio.NioDatagramChannel;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.BindException;
import java.net.InetSocketAddress;

/**
 * 启动UDP服务
 * @author Dujie
 * @create 2025/8/5
 * @since 1.0.0
 */
@Slf4j
public class UdpBridgeServer extends AbstractServer{
    private ChannelHandlerContext channelHandlerContext;
    private InetSocketAddress sender; // 获取客户端地址

    private Channel clientChannel;

    private int port;
    private EventLoopGroup eventLoopGroup;
    //监听端口的通道，即server的处理通道
    private BridgeMode bridgeMode;

    public UdpBridgeServer(int udpPort) {
        this.bridgeMode = BridgeMode.Server;
        this.port = udpPort;
    }

    public UdpBridgeServer(String targetHost, int udpPort) {
        this.sender = new InetSocketAddress(targetHost, udpPort);
        this.bridgeMode = BridgeMode.Client;
        this.port = udpPort+2;//client模式本地绑定端口+2
    }

    public UdpBridgeServer(String targetHost, int udpPort, PortAllocator portAllocator) {
        this.sender = new InetSocketAddress(targetHost, udpPort);
        this.bridgeMode = BridgeMode.Client;
        this.port = portAllocator.getUdp();
    }

    @Override
    public void start() throws Exception {
        log.debug("UdpBridgeServer server start.");
        eventLoopGroup = new NioEventLoopGroup();
        //启动类
        Bootstrap serverBootstrap = new Bootstrap();
        //组配置，初始化ServerBootstrap的线程组
        serverBootstrap.group(eventLoopGroup)
                //数据包通道，udp通道类型
                .channel(NioDatagramChannel.class)
                //支持广播
                .option(ChannelOption.SO_BROADCAST, true)
                //接收包缓存
                .option(ChannelOption.SO_RCVBUF, 4096)
                //通道处理者
                .handler(new ChannelInitializer<NioDatagramChannel>(){

                    @Override
                    protected void initChannel(NioDatagramChannel nioDatagramChannel) throws Exception {
                        ChannelPipeline pipeline = nioDatagramChannel.pipeline();
                        pipeline.addLast(new ServerHandler());
                    }
                });
        //Future：异步任务的生命周期，可用来获取任务结果
        // 绑定端口，开始接收进来的连接
        try {
//            serverBootstrap.remoteAddress(sender);
            //String localIp = NetUtils.localIP();
            clientChannel = serverBootstrap.bind(port).syncUninterruptibly().channel();
        }catch(Exception e){
            if(e instanceof BindException){
                throw new BindException("本地绑定端口:"+port+"异常:"+e.toString());
            }
            throw e;
        }
        log.info("[UdpBridgeServer] Server startup successful, port = {}", port);

        // 等待服务器 socket 关闭
        //future.channel().closeFuture().sync();


    }

    @Override
    public void stop() {
        log.debug("UDP服务关闭.");
        super.stop();
        try {
            if(channelHandlerContext != null){
                channelHandlerContext.close();
                channelHandlerContext = null;
            }
            if(clientChannel != null){
                clientChannel.close();
            }
            if(eventLoopGroup != null) {
                eventLoopGroup.shutdownGracefully();
            }
            log.info("UdpServer shutdown success");
        } catch (Exception e) {
            log.info("UdpServer shutdown fail {}", e);
        }
    }

    @Override
    public void send(ByteBuf buffer) {

        if (log.isDebugEnabled()) {
            log.debug("[UDP] 给地址:{}发送应答数据包: {}", sender, HexUtils.toHexString(ByteBufUtil.getBytes(buffer)));
        }
        if(bridgeMode == BridgeMode.Client) {
            if(clientChannel == null){
                log.warn("暂时没有客户端连接到UDP服务!");
                return;
            }

            clientChannel.writeAndFlush(new DatagramPacket(
                    buffer,
                    sender
            ));
        }else {
            if (channelHandlerContext == null || sender == null) {
                log.warn("暂时没有客户端连接到UDP服务!");
                return;
            }

            // 发送响应给客户端
            channelHandlerContext.writeAndFlush(new DatagramPacket(
                    buffer,
                    sender
            ));
        }
    }

    public class ServerHandler extends SimpleChannelInboundHandler<DatagramPacket> {

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket datagramPacket) throws Exception {
            // 获取客户端地址
            InetSocketAddress currSender = datagramPacket.sender();

            ByteBuf msg = datagramPacket.content();
            byte[] buffer = ByteBufUtil.getBytes(msg);
            if(log.isDebugEnabled()) {
                log.debug("收到客户端 {} 数据: {}", currSender, HexUtils.toHexString(buffer));
            }
            if(bridgeMode == BridgeMode.Server){
                sender = currSender;
            }
            //ctx.writeAndFlush("ok\n");
            //收到udp数据写入管道流，等待串口服务处理
            //UdpServer.this.getConnector().write(buffer, 0, buffer.length);
            packByteTo(buffer);
        }
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            super.channelActive(ctx);
            channelHandlerContext = ctx;
        }

        // 异常处理
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            log.error("客户端连接异常: {}" , cause);
            ctx.close();
        }
    }

    /**
     * 数据打包为UDP标记
     * @param buffer
     * @throws IOException
     */
    private void packByteTo(byte[] buffer) throws IOException {
        this.getConnector().write(packetParser.encode(new SimpleSerialCommPacket(StreamType.UDP, buffer)));
    }
}
